﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace SimplexEngine
{
    public class ContentSystem
    {
        private static ContentSystem instance = null;

        internal ContentSystem()
        {
            instance = this;

            foreach (ResourceType value in Enum.GetValues(typeof(ResourceType)))
            {
                directories.Add(value, new List<String>());
                directories[value].Add(value.ToString().Replace("_", ""));
            }

            // TODO : move to dll
            resource_factory.AddResourceLoader(new ImageResourceLoader());
            resource_factory.AddResourceLoader(new ShapeResourceLoader());
            resource_factory.AddResourceLoader(new ShaderResourceLoader());
            resource_factory.AddResourceLoader(new FontResourceLoader());
            resource_factory.AddResourceLoader(new MD2ResourceLoader());
            resource_factory.AddResourceLoader(new MD5ResourceLoader());
        }

        ~ContentSystem()
        {
            if (content_thread.IsAlive)
                content_thread.Abort();
        }

        private Dictionary<ResourceType, List<String>> directories = new Dictionary<ResourceType, List<string>>();

        public void AddDirectory(ResourceType type, String directory)
        {
            directories[type].Add(directory);
        }

        /// <summary>
        /// get resource
        /// </summary>
        /// <param name="resource_name">name of resource</param>
        /// <returns>resource</returns>
        public Resource GetResource(String resource_name)
        {
            lock (resources)
            {
                if (resources.ContainsKey(resource_name))
                    return resources[resource_name];
            }

            Resource result = new Resource(resource_name);

            lock (resources)
            {
                resources.Add(result.Name, result);
            }

            lock (queued_resources)
            {
                queued_resources.Add(result);
            }

            return result;
        }

        /// <summary>
        /// get resource immediately
        /// </summary>
        /// <param name="resource_name">name of resource</param>
        /// <returns>resource</returns>
        public Resource GetResourceImmediately(String resource_name)
        {
            return null;
        }

        // resource factory
        private ResourceFactory resource_factory = new ResourceFactory();

        // resource map
        private Dictionary<String, Resource> resources = new Dictionary<string, Resource>();

        // content thread
        private Thread content_thread = new Thread(ThreadLoadResources);

        // loading resources
        private static List<Resource> loading_resources = new List<Resource>();
        // queued resources
        private List<Resource> queued_resources = new List<Resource>();

        private static void ThreadLoadResources()
        {
            foreach (Resource resource in loading_resources)
                instance.LoadResource(resource);
        }

        private void LoadResources()
        {
            if (loading_resources.Count == 0)
                return;

            content_thread = new Thread(ThreadLoadResources);
            content_thread.Start();
        }

        private void LoadResource(Resource resource)
        {
            System.Diagnostics.Debug.Print("LoadResource " + resource.Name);

            ResourceType resource_type = resource_factory.GetResourceType(resource.Name);
            foreach (String directory in directories[resource_type])
            {
                String path = System.IO.Path.Combine(directory, resource.Name);
                Object result = resource_factory.LoadResource(path);
                if (result != null)
                {
                    resource.SetObject(result);
                    return;
                }
            }
        }

        private void CopyResources()
        {
            foreach (Resource resource in loading_resources)
            {
                if (resource.data != null)
                    resource.SetReady(true);
            }
            loading_resources.Clear();

            foreach (Resource resource in queued_resources)
            {
                if (!(resources.ContainsKey(resource.Name) && resources[resource.Name].IsReady()))
                    loading_resources.Add(resource);
            }
            queued_resources.Clear();
        }

        private void ReleaseResources()
        {
        }

        internal void Update(Microsoft.Xna.Framework.GameTime game_time)
        {
            if (!content_thread.IsAlive)
            {
                CopyResources();
                LoadResources();
            }

            ReleaseResources();
        }
    }
}
